package com.google.code.rgbgoesbetween.services;

import java.util.ArrayList;

import android.content.Context;
import android.util.Log;

import com.google.code.rgbgoesbetween.data.AccountStatus;
import com.google.code.rgbgoesbetween.data.Datastore;
import com.google.code.rgbgoesbetween.data.Item;
import com.google.code.rgbgoesbetween.data.Profile;
import com.google.code.rgbgoesbetween.data.QueryStatus;
import com.google.code.rgbgoesbetween.data.SearchEngine;
import com.google.code.rgbgoesbetween.data.SearchEngineCriteria;
import com.google.code.rgbgoesbetween.data.inmemory.InMemoryDatastore;
import com.google.code.rgbgoesbetween.data.sqlite.SQLiteDatastore;

/**
 * A <code>Singleton</code> pattern class responisible for interfacing with
 *  the data store and search engine.
 * 
 */
public class DataProvider {
	
	private static final int PROVIDE_ID = -10;
	// Singleton "stuff"
	private static DataProvider dataProvider;
	private DataProvider() {}
	private boolean initialized = false;
	
	/**
	 * Retrieves the singleton instance with which to do shenanigans to the data. Allows for the CRUD operations.
	 * 
	 * @return DataProvider the singleton instance
	 */
	public static DataProvider getDataProvider() {
		if (dataProvider == null) {
			dataProvider = new DataProvider();
		}
		return dataProvider;
	}
	
	private Datastore datastore;
	private SearchEngine searchEngine;
	
	/**
	 * Adds the given item to the data store.
	 * 
	 * @param item the item to be added to the data store
	 * @return the item that was added or null if nothing was added
	 */
	public Item createItem(Item item) {
		Log.d("DATA_PROVIDER", "Datastore: " + datastore.toString());
		return datastore.createItem(item);
	}

	/**
	 * 
	 * Creates the profile using the given username and password. The profile should NOT already exist.
	 * All data validation should already be done at this point.
	 * 
	 * @param username the name of the profile to be created
	 * @param passHash the passHash of the user to be created
	 * @return the profile that was created, null if no profile was created
	 */
	public Profile createProfile(String username, String passHash) {
		return this.createProfile(username,passHash, null, null, null, AccountStatus.STANDARD_MEMBER); // Only data we have are username and pas
	}
	
	/**
	 * 
	 * Creates the admin profile using the given username and password. The profile should NOT already exist.
	 * All data validation should already be done at this point.
	 * 
	 * @param username the name of the profile to be created
	 * @param passHash the passHash of the user to be created
	 * @return the profile that was created, null if no profile was created
	 */
	public Profile createAdminProfile(String username, String passhash) {
		return createProfile(username, passhash, null, null, null, AccountStatus.ADMIN_MEMBER);
	}
	
	/**
	 * 
	 * Creates the profile using the given username and password. The profile should NOT already exist. 
	 * All data validation should already be done at this point.
	 * 
	 * @param username the name of the profile to be created
	 * @param passHash the passHash of the user to be created
	 * @param address the address of the user to be created
	 * @param phone the phone number of the user to be created
	 * @param email the email address of the user to be created
	 * @param accountStatus the account status of the user to be created
	 * @return the profile that was created, null if no profile was created
	 */
	
	public Profile createProfile(String username, String passHash, 
			String address, String phone, String email, AccountStatus accountStatus) {
		Log.d("DATA_PROVIDER", "DatastoreName = " + datastore.getClass().getName());
		return datastore.createProfile(new Profile(PROVIDE_ID, username, passHash, accountStatus,
				address, phone, email));
		}
	
	/**
	 * 
	 * TODO implement this
	 * 
	 * @param item
	 * @return int return code
	 */
	
	public int deleteItem(Item item) {
		return 0;
	}
	
	/**
	 * 
	 * Gets the user with the associated username, if it exists. Otherwise, returns null.
	 * 
	 * @param username
	 * @return
	 */
	public Profile getProfile(String username) {
		return datastore.getAccountInfo(username);
	}
	
	/**
	 * Checks whether the data store has been initialized
	 * @return True if the data store has been initialized, false if not.
	 */
	public boolean isSearchInitialized() {
		return this.datastore != null;
	}
	
	/**
	 * Fetches the item with the corresponding id.
	 * @param id The id of the item to get
	 * @return Null if there is no item matching, or the item that matches.
	 */
	public Item requestItem(int id) {
		return this.datastore.requestItem(id);
	}
	/**
	 * Fetches each item with a matching id. 
	 * @param ids The ids to fetch. Should not contain duplicates.
	 * @return The corresponding items in the order of the ids given.
	 */
	public Item[] requestItemByIDs(Iterable<Integer> ids) {
		ArrayList<Item> array = new ArrayList<Item>();
		for(int id : ids)
			array.add(datastore.requestItem(id));
		return array.toArray(new Item[]{}); 
	}
	
	/**
	 * Updates the profile to be the given info.
	 * 
	 * @param profile The new profile data
	 * @return Whether or not the profile was updated successfully
	 */
	public QueryStatus updateProfile(Profile profile) {
		return datastore.updateProfile(profile);
	}
	
	/**
	 * Searches the data for items matching the given search criteria
	 * 
	 * @param criteria The search criteria to give. May contain null fields
	 * @return An int array of ITEM ids that matched the given search criteria.
	 */
	public int[] findMatchingIds(SearchEngineCriteria criteria) {
		return searchEngine.getMatchingIDs(criteria);
	}

	/**
	 * Guarantees that dependency is properly injected. Not thread safe though! The guarantee is loose.
	 */
	public void init(Context c) {
		if(!initialized) {
			initialized = true;
			SQLiteDatastore data = new SQLiteDatastore(c);
			datastore = data;
			searchEngine = data;
		}
	}
	
	/**
	 * Shifts to in memory data base. USE ONLY FOR TESTING !!!
	 */
	public void testInit() {
		datastore = new InMemoryDatastore();
		initialized = true;
	}
	
	/**
	 * De-initializes the data store and everything. Puts an end to any and all shenanigans.
	 */
	public void tearDown() {
		datastore = null;
		searchEngine = null;
		initialized = false;
	}

	/**
	 * Deletes the given profile from the data base.
	 * @param acct The account to delete
	 * @return QuereyStatus of the result of the attempt to delete
	 */
	public QueryStatus deleteProfile(Profile acct)
	{
		return datastore.deleteProfile(acct);
	}
	
}
